﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Qreed.Collections;

namespace Qreed.Windows.Forms
{
    public partial class WizardForm : Form
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="WizardForm"/> class.
        /// </summary>
        public WizardForm()
        {
            InitializeComponent();

            // Hook into the changes made to the pages collection

            _pages.OnChange += new EventHandler<NotifyOnChangeEventArgs<WizardPage>>(PagesCollectionChange);
        }

        void PagesCollectionChange(object sender, NotifyOnChangeEventArgs<WizardPage> e)
        {
            switch (e.EventType)
            {
                case NotifyOnChangeEventType.InsertItem:
                    e.Item.WizardForm = this;
                    break;

                case NotifyOnChangeEventType.RemoveItem:
                    e.Item.WizardForm = null;
                    break;
            }
        }

        /// <summary>
        /// Handles the Load event of the WizardForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void WizardForm_Load(object sender, EventArgs e)
        {
            ActiveButtons = WizardButtons.None;

            InitializeSize();

            if (Pages.Count > 0 && _activePage == null)
                ActivePageIndex = 0;
        }

        private void InitializeSize()
        {
            if (DesignMode) return;

            SuspendLayout();

            try
            {
                Size maxSize = new Size(0, 0);

                foreach (WizardPage page in _pages)
                {
                    maxSize.Width = Math.Max(maxSize.Width, page.Size.Width);
                    maxSize.Height = Math.Max(maxSize.Height, page.Size.Height);
                }

                foreach (WizardPage page in _pages)
                    page.Size = maxSize;

                Size extraSize = this.Size;
                extraSize -= contentPanel.Size;
                
                Size newSize = maxSize + extraSize;
                this.Size = newSize;
            }
            finally
            {
                ResumeLayout();
            }
        }

        private NotifyOnChangeCollection<WizardPage> _pages = new NotifyOnChangeCollection<WizardPage>();
        /// <summary>
        /// Gets the pages.
        /// </summary>
        /// <value>The pages.</value>
        public IList<WizardPage> Pages
        {
            get { return _pages; }
        }

        /// <summary>
        /// Finds the page with the provided name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>The page with the provided name or <c>null</c> if the page has not been found.</returns>
        public WizardPage FindPage(string name)
        {
            foreach (WizardPage page in Pages)
                if (page.Name == name)
                    return page;

            return null;
        }

        private WizardPage _activePage;
        /// <summary>
        /// Gets the active page.
        /// </summary>
        /// <value>The active page.</value>
        public WizardPage ActivePage
        {
            get { return _activePage; }
            set
            {
                if (value == null)
                    throw (new ArgumentNullException());

                if (!Pages.Contains(value))
                    throw new Exception("Can't set the active page to a page which is not in the list.");

                SuspendLayout();

                try
                {
                    foreach (WizardPage page in Pages)
                        page.Visible = false;

                    WizardPage oldActivePage = _activePage;
                    _activePage = value;

                    if (!contentPanel.Controls.Contains(_activePage))
                    {
                        contentPanel.Controls.Add(_activePage);
                        _activePage.Dock = DockStyle.Fill;
                    }

                    _activePage.Visible = true;

                    WizardButtons buttons = GetButtonStateForPage(_activePage);

                    WizardPageActivatedEventArgs e = new WizardPageActivatedEventArgs(_activePage, buttons);

                    _activePage.onSetActive(e);

                    if (e.Cancel)
                    {
                        _activePage.Visible = false;
                        _activePage = oldActivePage;

                        if (_activePage != null)
                            _activePage.Visible = true;
                    }
                    else
                    {
                        ActiveButtons = e.Buttons;
                    }
                }
                finally
                {
                    ResumeLayout(true);
                }
            }
        }

        private WizardButtons GetButtonStateForPage(WizardPage page)
        {
            WizardButtons buttons = WizardButtons.None;

            int index = Pages.IndexOf(page);

            if (index > 0)
                buttons |= WizardButtons.Back;

            if (index < Pages.Count - 1)
                buttons |= WizardButtons.Next;

            if (index == Pages.Count - 1)
                buttons |= WizardButtons.Finish;

            return buttons;
        }

        /// <summary>
        /// Gets or sets the index of the active page.
        /// </summary>
        /// <value>The index of the active page.</value>
        public int ActivePageIndex
        {
            set
            {
                if (value < 0 || value >= Pages.Count)
                    throw (new ArgumentOutOfRangeException());

                ActivePage = Pages[value];
            }
            get
            {
                for (int i = 0; i < Pages.Count; i++)
                {
                    if (Pages[i] == _activePage)
                        return i;
                }

                return -1;
            }
        }

        private WizardButtons _activeButtons = WizardButtons.None;
        /// <summary>
        /// Gets or sets the active buttons.
        /// </summary>
        /// <value>The active buttons.</value>
        public WizardButtons ActiveButtons
        {
            get { return _activeButtons; }
            set
            {
                _activeButtons = value;

                SuspendLayout();

                try
                {
                    buttonBack.Enabled = (value & WizardButtons.Back) > 0;

                    if ((value & WizardButtons.Finish) > 0)
                    {
                        buttonFinish.Enabled = true;
                        buttonFinish.Visible = true;

                        buttonNext.Enabled = false;
                        buttonNext.Visible = false;

                        if(!DesignMode)
                            AcceptButton = buttonFinish;
                    }
                    else
                    {
                        buttonFinish.Enabled = false;
                        buttonFinish.Visible = false;

                        buttonNext.Enabled = (value & WizardButtons.Next) > 0;
                        buttonNext.Visible = true;

                        if(!DesignMode)
                            AcceptButton = buttonNext;
                    }
                }
                finally
                {
                    ResumeLayout();
                }
            }
        }

        private void buttonNext_Click(object sender, EventArgs e)
        {
            int index = ActivePageIndex + 1;

            if (index >= Pages.Count)
                return;

            WizardPage newPage = Pages[index];

            WizardPageEventArgs wpe = new WizardPageEventArgs(newPage);

            ActivePage.onWizardNext(wpe);

            if (!wpe.Cancel)
            {
                ActivePage = FindPage(wpe.NewPageName);
            }
        }

        private void buttonBack_Click(object sender, EventArgs e)
        {
            int index = ActivePageIndex - 1;

            if (index <= -1)
                return;

            WizardPage newPage = Pages[index];

            WizardPageEventArgs wpe = new WizardPageEventArgs(newPage);

            ActivePage.onWizardBack(wpe);

            if (!wpe.Cancel)
            {
                ActivePage = FindPage(wpe.NewPageName);
            }
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            CancelEventArgs ce = new CancelEventArgs(false);
            ActivePage.onWizardCancel(ce);
        }

        private void buttonFinish_Click(object sender, EventArgs e)
        {
            CancelEventArgs ce = new CancelEventArgs();
            ActivePage.onWizardFinish(ce);
        }
    }
}
